package re

import "eh/sm"

import "fmt"

type RE interface {
  /*
    from the state, to all of the given status, in this SM.
  */
  transitions( *sm.SM, sm.State, sm.States )
}

func MakeSMFor( re RE ) *sm.SM {
  m := sm.NewSM()
  start, finish := m.NewState(), m.NewState()
  re.transitions( m, start, sm.States{finish: sm.Null{}} )
  return m
}

func MakeLexerFor( them []RE ) *sm.SM {
  m := sm.NewSM()
  start := m.NewState()
  for i, re := range them {
    finish := m.NewState()
    re.transitions( m, start, sm.States{finish: sm.Null{}} )
    m.EndState( finish, i )
  }
  return m 
}

type AChar struct {ch int}

func (x AChar) transitions( m *sm.SM, from sm.State, toAll sm.States ) {
  for to, _ := range toAll { m.Transition( x.ch, from, to ) }
}

type Alt []RE

func (x Alt) transitions( m *sm.SM, from sm.State, toAll sm.States ) {
  for _, operand := range x { operand.transitions( m, from, toAll ) }
}

type Seq []RE

func (x Seq) transitions( m *sm.SM, from sm.State, toAll sm.States ) { 
  switch len(x) {
    case 0:
      m.Epsilon( from, toAll )
    
    case 1:
      x[0].transitions( m, from, toAll )
    
    default:
      middle := m.NewState()
      Seq(x[1:]).transitions( m, middle, toAll )
      x[0].transitions( m, from, make(sm.States).With(middle) )
  }  
} 

type AnyOf struct {them string; inverted bool}

func (x AnyOf) transitions( m *sm.SM, from sm.State, toAll sm.States ) {
  if x.inverted {
    for to, _ := range toAll { m.InvertedTransition( x.them, from, to ) }
  } else {
    for _, ch := range x.them {
      for to, _ := range toAll { m.Transition( ch, from, to ) }
    }
  }
}

func (a AnyOf) String() string {
  var which string; if a.inverted { which = "But" } else { which = "Of" }
  return fmt.Sprintf( "Any%s{%s}", which, a.them )
}

type REP struct {rep int; repeated RE}

func (r REP) String() string {
  return fmt.Sprintf( "(%v)%c", r.repeated, r.rep )
}

func (x REP) transitions( m *sm.SM, from sm.State, toAll sm.States ) {
  xr := x.repeated
  switch x.rep {
    case '?':
        xr.transitions( m, from, toAll )
        m.Epsilon( from, toAll )
                
    case '*':
        loop := m.NewState()
        xr.transitions( m, loop, toAll.With(loop) )
        m.Epsilon( from, toAll.With(loop) )
        
    case '+':
        loop := m.NewState()
        xr.transitions( m, loop, toAll.With(loop) )
        m.Epsilon( from, make(sm.States).With(loop) )
  }
}

func (x AChar) String() string {
  return "'" + string(x.ch) + "'"
}

